import { Transformer } from "./Transformer";

//对称结构
//类似 { } #if #endif
namespace Symmetric {
    export interface InfSymmetric {
        start: number[],
        end: number[],
    }

    export function CountSymmetric(text: string, regStart: RegExp, regEnd: RegExp) {
        let sym: InfSymmetric = {
            start: [],
            end: [],
        };

        let stack: number[] = [];
        let starts = [...text.matchAll(regStart)];
        let ends = [...text.matchAll(regEnd)];
        let merges = [...starts, ...ends].sort((a, b) => a.index - b.index);

        let item: RegExpMatchArray;
        for (let i = 0, length = merges.length; i < length; i++) {
            item = merges[i];
            if (starts.indexOf(item) >= 0)
                stack.push(item.index);
            else if (ends.indexOf(item) >= 0) {
                sym.start.push(stack.pop());
                sym.end.push(item.index + item[0].length);
            }
        }

        return sym;
    }

    export function GetSymmetricPos(sym: InfSymmetric, startPos: number, reverse: boolean = false) {
        if (reverse) {
            let index = sym.end.indexOf(startPos);
            return sym.start[index];
        } else {
            let index = sym.start.indexOf(startPos);
            return sym.end[index];
        }
    }
}

export namespace Structures {

    //结构都是以类似 '{' 开头 以 '}' 结尾
    const Regs = {
        shader: /Shader ".+" \{/g,
        properties: /Properties \{/g,
        subShader: /SubShader \{/g,
        pass: /Pass \{/g,
        programFp: /Program "fp" \{/g,
        programVp: /Program "vp" \{/g,
        subProgram: /SubProgram ".+ " \{/g,
        keywords: /Keywords \{/g,
        subProgramTier: /SubProgram "(.+) " \{/,

        vert: /#ifdef VERTEX/g,
        frag: /#ifdef FRAGMENT/g,
        main: /void main\(\)\n\s+\{/g,
    };

    class Structure {

        //括号
        private _braces: Symmetric.InfSymmetric;
        public get braces(): Symmetric.InfSymmetric {
            if (this._braces == null)
                this._braces = Symmetric.CountSymmetric(this.subContent, /\{/g, /\}/g);
            return this._braces;
        }

        //宏定义
        private _defs: Symmetric.InfSymmetric;
        public get defs(): Symmetric.InfSymmetric {
            if (this._defs == null)
                this._defs = Symmetric.CountSymmetric(this.subContent, /(#ifdef|#if)/g, /#endif/g);
            return this._defs;
        }

        //子串
        get subContent() { return this.content.substring(this.start, this.end); }

        //每个结构都指向一个文件文本,使用开始位置和结束位置获取子串,节省内存
        constructor(protected content: string, private start: number, private end: number) { }

        GetSymStructure<T extends Structure>(sym: Symmetric.InfSymmetric, reg: RegExp, Type: new (content: string, start: number, end: number) => T, head: boolean = false): T[] {
            let subContent = this.subContent;
            let paragraps = [...subContent.matchAll(reg)];
            if (paragraps.length <= 0) return [];
            let structures: T[] = [];
            for (let i = 0, length = paragraps.length; i < length; i++) {
                let pragraph = paragraps[i];
                let start = pragraph?.index ?? 0;
                let length = head ? 0 : pragraph[0].length - 1;
                let end = Symmetric.GetSymmetricPos(sym, start + length);
                structures.push(new Type(this.content, this.start + start, this.start + end));
            }
            return structures;
        }

        //获取子结构
        protected GetBracesStructure<T extends Structure>(reg: RegExp, Type: new (content: string, start: number, end: number) => T): T[] {
            return this.GetSymStructure(this.braces, reg, Type);
        }

        //宏定义子结构
        protected GetIfDefStructure<T extends Structure>(reg: RegExp, Type: new (content: string, start: number, end: number) => T): T[] {
            return this.GetSymStructure(this.defs, reg, Type, true);
        }

    }

    //Shader文件结构 通常一个文件包含一个 Shader
    export class ShaderFile extends Structure {
        shader: Shader;
        constructor(text: string) {
            super(text, 0, text.length);
            this.shader = this.GetBracesStructure(Regs.shader, Shader)[0];
        }

        get complete() { return this.shader.complete; }
    }

    //Shader结构 包含多个 SubShader
    class Shader extends Structure {
        subShaders: SubShader[];
        constructor(content: string, start: number, end: number) {
            super(content, start, end);
            this.subShaders = this.GetBracesStructure(Regs.subShader, SubShader);
        }

        get complete() {
            let subContent = this.subContent;
            for (let i = 0, length = this.subShaders.length; i < length; i++)
                subContent = subContent.replace(this.subShaders[i].subContent, this.subShaders[i].complete);
            return subContent;
        }
    }

    //SubShader结构 包含多个 pass
    class SubShader extends Structure {
        passes: Pass[];
        constructor(content: string, start: number, end: number) {
            super(content, start, end);
            this.passes = this.GetBracesStructure(Regs.pass, Pass);
        }

        get complete() {
            let subContent = this.subContent;
            for (let i = 0, length = this.passes.length; i < length; i++)
                subContent = subContent.replace(this.passes[i].subContent, this.passes[i].complete);
            return subContent;
        }
    }

    //Pass结构 包含 Vp 和 Fp
    class Pass extends Structure {
        programFp: ProgramFp;
        programVp: ProgramVp;
        constructor(content: string, start: number, end: number) {
            super(content, start, end);
            this.programFp = this.GetBracesStructure(Regs.programFp, ProgramFp)[0];
            this.programVp = this.GetBracesStructure(Regs.programVp, ProgramVp)[0];
        }

        get complete() {
            let subContent = this.subContent;
            if (this.programVp != null) subContent = subContent.replace(this.programVp.subContent, this.programVp.complete);
            if (this.programFp != null) subContent = subContent.replace(this.programFp.subContent, this.programFp.complete);
            subContent = subContent.replace(/GpuProgramID \d+/g, '');
            return subContent;
        }
    }

    //Fp结构 包含多个 SubProgram
    class ProgramFp extends Structure {
        subPrograms: SubProgram[];
        constructor(content: string, start: number, end: number) {
            super(content, start, end);
            this.subPrograms = this.GetBracesStructure(Regs.subProgram, SubProgram);
        }

        get complete() { return ''; }
    }

    //Vp结构 包含多个 SubProgram
    class ProgramVp extends Structure {
        subPrograms: SubProgram[];
        constructor(content: string, start: number, end: number) {
            super(content, start, end);
            this.subPrograms = this.GetBracesStructure(Regs.subProgram, SubProgram);
        }

        //取keywords最长的 SubProgram
        getLongestKeywordsSubProgram(programs: SubProgram[]) {
            let keywordsLengths = programs.map(p => p.Keywords?.subContent.length ?? 0);
            let maxLength = Math.max(...keywordsLengths);
            let maxIndex = keywordsLengths.indexOf(maxLength);
            return programs[maxIndex];
        }

        get complete() {
            let targets = this.subPrograms.filter(p => p.tier == 'gles3 hw_tier02');
            if (targets.length <= 0) targets = this.subPrograms.filter(p => p.tier == 'gles hw_tier02');
            if (targets.length <= 0) targets = this.subPrograms;
            let target = this.getLongestKeywordsSubProgram(targets);
            return target?.complete;
        }
    }

    //SubProgram结构 包含 vert 和 frag
    //Shader实际代码 keywords变体实现
    export class SubProgram extends Structure {
        tier: string;
        Keywords: Structure;

        private _vert: Structure;
        public get vert(): Structure {
            if (this._vert == null) this._vert = this.GetIfDefStructure(Regs.vert, Structure)[0];
            return this._vert;
        }

        private _frag: Structure;
        public get frag(): Structure {
            if (this._frag == null) this._frag = this.GetIfDefStructure(Regs.frag, Structure)[0];
            return this._frag;
        }

        constructor(content: string, start: number, end: number) {
            super(content, start, end);
            this.Keywords = this.GetBracesStructure(Regs.keywords, Structure)[0];
            this.tier = this.subContent.match(Regs.subProgramTier)[1];
        }

        get complete() {
            return Transformer.GLSL2CG(this.vert.subContent, this.frag.subContent);
        }
    }
}